package com.quectel.util.kit;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * 数据库分页查询帮助类
 * 里边方法会向原先容器添加四个参数 limit offset pageNo pageSize
 *
 * @author: caolu
 * @email: louis.cao@quectel.com
 * @date: 2022-07-19 16:31:00
 */
@Slf4j
public class PageFetchKit {
    private static final String LIMIT = "limit", OFFSET = "offset", PAGE_NO = "pageNo", PAGE_SIZE = "pageSize";
    private static final int DEFAULT_PER_FETCH_SIZE = 1000;

    /**
     * <pre>
     * 分页查询参数
     * List<UserDto> result  = PageFetchKit.fetchAll(reqParams -> userService.queryList(reqParams), request.getParams());
     * </pre>
     *
     * @param execFunction 查询条件表达式
     * @param conditions   原始条件
     * @param <T>
     * @return
     */
    public static <T> List<T> fetchAll(Function<Map<String, Object>, List<T>> execFunction, Map<String, Object> conditions) {
        return fetchAll(execFunction, conditions, DEFAULT_PER_FETCH_SIZE);
    }

    /**
     * <pre>
     * 分页查询参数
     * List<UserDto> result  = PageFetchKit.fetchAll(reqParams -> userService.queryList(reqParams), request.getParams(),500);
     * </pre>
     *
     * @param execFunction   查询条件表达式
     * @param originalParams 原始条件
     * @param fetchSize      每次查询的数量
     * @param <T>
     * @return
     */
    public static <T> List<T> fetchAll(Function<Map<String, Object>, List<T>> execFunction, Map<String, Object> originalParams, int fetchSize) {
        long t1 = System.currentTimeMillis();
        List<T> result = new ArrayList<>();
        fetch(execFunction, originalParams, fetchSize, result::addAll);
        long t2 = System.currentTimeMillis();
        log.debug("PageFetchKit fetchAll method. Fetch {} every time, total cost {} ms, total count is {}.", fetchSize, t2 - t1, result.size());
        return result;
    }


    /**
     * <pre>
     * 分页查询参数 区别于上边两个每一批一个回调
     * List<UserDto> result  = PageFetchKit.fetch(reqParams -> userService.queryList(reqParams), request.getParams(),500,result->{});
     * </pre>
     *
     * @param execFunction   查询条件表达式
     * @param originalParams 原始条件
     * @param fetchSize      每次查询的数量
     * @param consumer       每一批数据的回调 禁止在consumer中删除当前正在被查询的数据 不然会造成数据漂移 里边可以做一些推送
     * @param <T>
     */
    private static <T> void fetch(Function<Map<String, Object>, List<T>> execFunction, Map<String, Object> originalParams, int fetchSize, Consumer<List<T>> consumer) {
        Map<String, Object> params = new HashMap<>(originalParams);
        int pageNo = 0;
        params.put(LIMIT, fetchSize);
        params.put(PAGE_SIZE, fetchSize);
        while (true) {
            ++pageNo;
            params.put(PAGE_NO, pageNo);
            params.put(OFFSET, (pageNo - 1) * fetchSize);

            long t1 = System.currentTimeMillis();
            List<T> applyResult = execFunction.apply(params);
            long t2 = System.currentTimeMillis();
            log.debug("PageFetchKit fetch method. Current pageNo is {},pageSize:{},total cost {} ms, return records count is {}.",
                    pageNo, PAGE_SIZE, t2 - t1, applyResult == null ? 0 : applyResult.size());

            if (CollectionUtils.isEmpty(applyResult)) {
                break;
            }
            consumer.accept(applyResult);
            if (applyResult.size() < fetchSize) {
                break;
            }
        }
    }

}
